/*
 * Copyright 2007 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.gwt.user.rebind.ui;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;

import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;


/**
 * Builds the .xml file for the gadget.
 */
class GadgetBuilder {

  private class UserPref {
    
    private String name     = null;
    private String displayName = null;
    private boolean required = false;
    private String defaultValue = null;
    private String dataType = null;
    
    public UserPref(JMethod method) {
      this.name = method.getName().substring(3);
      this.dataType = method.getReturnType().toString();
      parseMetaData(method);
    }
    
    public String getDataType() {
      return dataType;
    }
    
    public String getDefaultValue() {
      return defaultValue;
    }
    
    public String getDisplayName() {
      return displayName;
    }
    
    public String getName() {
      return name;
    }
    
    public boolean isRequired() {
      return required;
    }
    
    private void parseMetaData(JMethod method) {
      String[] metadataTags = method.getMetaDataTags();
      for(int tag = 0; tag < metadataTags.length; ++tag) {
        int periodLocation = metadataTags[tag].indexOf('.');
        if(periodLocation == -1){
          continue;
        }
        
        String tagPrefix = metadataTags[tag].substring(0, periodLocation);
        if(tagPrefix.compareTo("gadget") != 0)
          continue;
        
        String[][] md = method.getMetaData(metadataTags[tag]);
        if ((md.length > 1) || (md.length == 1 && md[0].length != 1))
          continue;
        
        String tagName = metadataTags[tag].substring(periodLocation+1);
        if(tagName.compareTo("display_name")==0)
          displayName = md[0][0];
        else if(tagName.compareTo("datatype")==0)
          dataType = md[0][0];
        else if(tagName.compareTo("required")==0)
          required = true;
        else if(tagName.compareTo("default_value")==0)
          defaultValue = md[0][0];
      }
    }
    
  }
  private String    gadgetName;
  private List       modulePrefsList = new ArrayList();
  
  private List      userPrefsList   = new ArrayList();
  
  public GadgetBuilder(String gadgetName) {
    this.gadgetName = gadgetName;
  }
 
  /**
   * Assimilates the user and module prefs that the user has added to the 
   * gadget.
   * 
   * @param logger a hierarchical logger which logs to the hosted console
   * @param method the JMethod that will be added to the list
   * 
   */
  public void assimilate(TreeLogger logger, JMethod method)
      throws UnableToCompleteException {

    if (method.getName().equals("setModulePrefs")){
      populateModulePrefsMap( method);
      return;
    }
    /* if the method is not setModulePrefs it is a user prefs method*/
   
    UserPref userPref = new UserPref(method);
    userPrefsList.add(userPref);
  }

  public String writeGadget(TreeLogger logger, GeneratorContext context, JClassType userType)
      throws UnableToCompleteException {

    // Create the gadget.
    String gadgetFileName = gadgetName + ".xml";
    OutputStream outStream = context.tryCreateResource(logger, gadgetFileName);
    if(outStream != null) {
      PrintWriter output = new PrintWriter(outStream);
      String scriptName = userType.getEnclosingType().getQualifiedSourceName() + ".nocache.js";
      scriptName = fixScriptName(scriptName);
      try {
        output.print("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
        output.print("<Module>\n");
        writeModulePrefs(output);
        writeUserPrefs(output);
        output.print("  <Content type=\"html\">\n");
        output.print("    <![CDATA[\n");
        output.print("      <script type=\"text/javascript\">\n");
        output.print("        var prefs = new _IG_Prefs();\n");
        output.print("      </script>\n");
        output.print("      <script language=\'javascript\' src=\"" + scriptName + "\" />\n");
        output.print("    ]]>\n");
        output.print("  </Content>\n");
        output.print("</Module>\n");
         
        output.flush();
        // Commit the stream.
        context.commitResource(logger, outStream);
      } catch (IOException e) {
        logger.log(TreeLogger.ERROR, "Failed while writing", e);
        throw new UnableToCompleteException();
      }
    } else {
      logger.log(
        TreeLogger.TRACE,
        "Generated gadget file already exists; Please delete " + gadgetFileName +" from the output directory manually.",
        null);
    }

    return gadgetFileName;
  }
  
  private String fixScriptName(String scriptName) {
    int startLoc = scriptName.indexOf(".client.");
    String firstHalf = scriptName.substring(0, startLoc);
    String secondHalf = scriptName.substring(startLoc + 7);
    
    return firstHalf + secondHalf;
  }
  
  private void populateModulePrefsMap(JMethod method) {
    String[] metadataTags = method.getMetaDataTags();
    for(int tag = 0; tag < metadataTags.length; ++tag) {
      int periodLocation = metadataTags[tag].indexOf('.');
      if(periodLocation == -1){
        continue;
      }
      
      String tagPrefix = metadataTags[tag].substring(0, periodLocation);
      if(tagPrefix.compareTo("gadget") != 0)
        continue;
      
      String[][] md = method.getMetaData(metadataTags[tag]);
      String OutputString = md[0][0];
      if ((md.length > 1) || (md.length == 1 && md[0].length != 1))
        for(int word = 1; word < md[0].length; ++word) {
          OutputString += " " + md[0][word];
        }
      
      modulePrefsList.add(metadataTags[tag].substring(periodLocation+1) + "=\"" + OutputString +"\"");
    }
  }

  private void writeModulePrefs(PrintWriter output)throws IOException {
    output.print("  <ModulePrefs \n");
    for (Iterator iter = modulePrefsList.iterator(); iter.hasNext();) {
      output.print((String)iter.next() + " \n");
    }
    output.print("  />\n");
  }
  
  private void writeUserPrefs(PrintWriter output)throws IOException {
    for (Iterator iter = userPrefsList.iterator(); iter.hasNext();) {
      UserPref curPref = (UserPref)iter.next();
      
      output.print("  <UserPref name=\"" + curPref.getName() + "\" ");
      if( curPref.getDisplayName() != null)
        output.print("display_name=\"" + curPref.getDisplayName() + "\" ");
      
      if(curPref.getDataType() != null && ( curPref.getDataType() != "String" || curPref.getDataType() != "int"))
        output.print("datatype=\"" + curPref.getDataType() + "\" ");
      
      if(curPref.isRequired())
        output.print("required=\"true\" ");
      
      if(curPref.getDefaultValue() != null)
        output.print("default_value=\"" + curPref.getDefaultValue() + "\" ");
      
      output.print(" />\n");
    }
  }
  

}
